package com.sheep.advanceddemo.config;

import com.sheep.advanceddemo.service.SysUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import org.springframework.security.web.session.SessionInformationExpiredStrategy;

import javax.sql.DataSource;

// 表明这是一个配置类
@Configuration
// 开启Spring Security对WebMVC的支持
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private SysUserDetailsService sysUserDetailsServiceImpl;

    @Autowired
    private BCryptPasswordEncoder passwordEncoder;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(sysUserDetailsServiceImpl).passwordEncoder(passwordEncoder);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //设置X-Frame-Options响应头为SAMEORIGIN
        http.headers().frameOptions().sameOrigin();
        //放行不用权限的资源（去登录页面当然不需要用权限，否则你都看不到登录界面，还怎么登录，所以去登录界面必须放行）
        http.authorizeRequests().antMatchers("/toLogin").permitAll();
        //拦截需要权限的资源（拦截所有请求，要想访问，登录的账号必须拥有USER和ADMIN的角色才行）
        http.authorizeRequests().antMatchers("/**").hasAnyRole("USER", "ADMIN").anyRequest().authenticated();
        //设置自定义登录界面
        //启用表单登录
        http.formLogin()
                //登录页面地址，只要你还没登录，默认就会来到这里
                .loginPage("/toLogin")
                //登录处理程序，Spring Security内置控制器方法
                .loginProcessingUrl("/login")
                //登录表单form中用户名输入框input的name名，不修改的话默认是username
                .usernameParameter("username")
                //登录表单form中密码框输入框input的name名，不修改的话默认是password
                .passwordParameter("password")
                //登录认证成功后默认转跳的路径
                .defaultSuccessUrl("/main")
                //登录成功跳转地址，使用的是请求转发
                //.successForwardUrl("/main")
                //登录失败跳转地址，使用的是请求转发
                .failureForwardUrl("/toLogin")
                .permitAll();
        //设置自定义登出界面
        //启用退出登录
        http.logout()
                //退出处理程序，Spring Security内置控制器方法
                .logoutUrl("/logout")
                //退出成功跳转地址
                .logoutSuccessUrl("/toLogin")
                //清除当前会话
                .invalidateHttpSession(true)
                //删除当前Cookie
                .deleteCookies("JSESSIONID")
                .permitAll();
        //关闭CSRF跨站点请求仿造保护
        http.csrf().disable();
        //开启记住我功能（自动登录）
        http.rememberMe()
                //表单参数名，默认参数是remember-me
                .rememberMeParameter("remember-me")
                //浏览器存的cookie名，默认是remember-me
                .rememberMeCookieName("remember-me")
                //保存30两天，默认是两周
                .tokenValiditySeconds(60 * 60 * 24 * 30)
                //使用数据库存储token，防止重启服务器丢失数据，非常重要，没有他不能保存到数据库
                .tokenRepository(persistentTokenRepository());
        //异常处理，使用函数表达式的写法可以不用在单独写一个类，非常方便
//        http.exceptionHandling()
//                .accessDeniedHandler((request, response, ex) -> {
//                    response.setStatus(HttpServletResponse.SC_FORBIDDEN);
//                    response.setHeader("Content-Type", "application/json;charset=utf-8");
//                    PrintWriter out = response.getWriter();
//                    out.write("{\"status\":\"error\",\"msg\":\"权限不足，请联系管理员!\"}");
//                    out.flush();
//                    out.close();
//                });

        //单用户登录，如果有一个登录了，同一个用户在其他地方不能登录
        //http.sessionManagement().maximumSessions(1).maxSessionsPreventsLogin(true);
        //单用户登录，如果有一个登录了，同一个用户在其他地方登录将前一个剔除下线
        //http.sessionManagement().maximumSessions(1).expiredUrl("/toLogin");
        //开启CORS
        //http.cors();
        //关闭CORS
        //http.cors().disable();
    }

    //数据源是咱们默认配置的数据源，直接注入进来就行
    @Autowired
    private DataSource dataSource;

    @Bean
    public PersistentTokenRepository persistentTokenRepository() {
        JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
        jdbcTokenRepository.setDataSource(dataSource);
        return jdbcTokenRepository;
    }

    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    public void configure(WebSecurity web) {
        web.ignoring().antMatchers("/css/**");
        web.ignoring().antMatchers("/img/**");
        web.ignoring().antMatchers("/js/**");
        web.ignoring().antMatchers("/favicon.ico");
    }
}
